home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / web / cweb.lha / cweb / examples / treeprint.web < prev    next >
Encoding:
Text File  |  1990-07-13  |  6.6 KB  |  244 lines

  1. Copyright 1987 Norman Ramsey -- Rutgers University
  2.  
  3. \def\vbar{\.{|}}
  4. @*Directory Trees.
  5. The object is to print out a directory hierarchy in some pleasant way.
  6. The program takes output from {\tt find * -type d -print \vbar\ sort}
  7. @^system dependencies@>
  8. and produces a nicer-looking listing.
  9. For those of you who may not have {\tt find} or {\tt sort}, the output is a 
  10. list of fully  
  11. qualified directory names (parent and child separated by slashes |'/'|),
  12. and everything is already nicely sorted in lexicographic order.
  13.  
  14. |treeprint| takes one option, |"-p"|, which tells it to use the printer's
  15. line-drawing set, rather than the terminal's.
  16. @c
  17. @<Global definitions@>@;
  18. @<Global include files@>@;
  19. @<Global declarations@>@;
  20.  
  21. @#
  22. main(argc, argv)
  23.      int argc;
  24.      char **argv;
  25. {
  26. @<|main| variable declarations@>;
  27. @<Search for options and set special characters on |"-p"|@>;
  28. @<Read output from find and enter into tree@>;
  29. @<Write tree on standard output@>@;
  30. exit(0);
  31. }
  32.  
  33. @
  34. We make all the siblings of a directory a linked list off of its left child, 
  35. and the offspring a linked list off the right side.
  36. Data are just directory names.
  37. @d sibling left
  38. @d child right
  39.  
  40. @<Global decl...@>=
  41. typedef struct tnode {
  42.   struct tnode *left, *right;
  43.   char *data;
  44. } TNODE;
  45. @ @<|main| variable...@>=
  46. struct tnode *root;
  47.  
  48.  
  49.  
  50. @*Input.
  51. Reading the tree is simple---we read one line at a time, and call on the 
  52. recursive |add_tree| procedure.
  53. @c
  54. read_tree (fp, rootptr)
  55.    FILE *fp;
  56.    struct tnode **rootptr;
  57. {
  58.    char buf[255], *p;
  59.  
  60.    while ((fgets(buf, 255, fp))!=NULL) {
  61.      @<If |buf| contains a newline, make it end there@>;
  62.      add_tree(rootptr, buf);
  63.    }
  64.  }
  65. @ @<Global include...@>=#include <stdio.h>
  66.  
  67. @ Depending what system you're on, you may or may not get a newline in |buf|.
  68. @<If |buf| contains a newline...@>=
  69.      p=buf; while (*p!='\0'&&*p!='\n') p++;
  70. @^system dependencies@>
  71.      *p='\0';
  72.  
  73. To add a string, we split off the first part of the name and insert it into 
  74. the sibling list. We then do the rest of the string as a child of the new node.
  75. @c
  76. add_tree(rootptr, p)
  77.      struct tnode **rootptr;
  78.      char *p;
  79. {
  80.      char *s;
  81.      int slashed;
  82.  
  83.      if (*p=='\0') return;
  84.  
  85. @<Break up the string so |p| is the first word,
  86.      |s| points at null-begun remainder,
  87.     and |slashed| tells whether |*s=='/'| on entry@>;
  88.  
  89.      if (*rootptr==NULL) {
  90. @<Allocate new node to hold string of size |strlen(p)|@>;
  91.        strcpy((*rootptr)->data,p);
  92.      } 
  93.      if (strcmp((*rootptr)->data,p)==0) {
  94.            if (slashed) ++s;
  95.            add_tree(&((*rootptr)->child),s);
  96.      }
  97.        else {
  98.            if (slashed) *s='/';
  99.            add_tree(&((*rootptr)->sibling),p);
  100.      }
  101.    }
  102.  
  103. @ We perform some nonsense to cut off the string |p| so that |p| just holds
  104. the first word of a multiword name. |s| points at what was either the end 
  105. of |p| or a slash delimiting names. In either case |*s| is made |'\0'|.
  106. Later depending on wether we want to pass the whole string or the last piece,
  107. we will restore the slash or advance |s| one character to the right.
  108.  
  109. @<Break up...@>=
  110.      for (s=p;*s!='\0'&&*s!='/';) s++;
  111.      if (*s=='/') {
  112.        slashed=1;
  113.        *s='\0';
  114.      } else slashed=0;
  115.  
  116. @ Node allocation is perfectly standard...
  117. @<Allocate new node...@>=
  118.        *rootptr=(struct tnode *) malloc (sizeof(struct tnode));
  119.        (*rootptr)->left = (*rootptr)->right = NULL;
  120.        (*rootptr)->data = malloc (strlen(p)+1);
  121.  
  122. @
  123. @<Global decl...@>= char *malloc();
  124.  
  125. @ In this simple implementation, we just read from standard input.
  126. @<Read...@>= read_tree(stdin,&root);
  127.  
  128. @*Output.
  129. We begin by defining some lines, tees, and corners.
  130. The |s| stands for screen and the |p| for printer.
  131. You will have to change this for your line-drawing set.
  132. @^system dependencies@>
  133.  
  134. @<Global definitions@>=
  135. #define svert '|'
  136. #define shoriz '-'
  137. #define scross '+'
  138. #define scorner '\\' /* lower left corner */
  139.  
  140. #define pvert '|'
  141. #define phoriz '-'
  142. #define pcross '+'
  143. #define pcorner '\\' /* lower left corner */
  144.  
  145. @ The default is to use the terminal's line drawing set.
  146. @<Global declarations@>=
  147. char vert=svert;
  148. char horiz=shoriz;
  149. char cross=scross;
  150. char corner=scorner;
  151.  
  152. @ With option |"-p"| use the printer character set.
  153. @<Search for options...@>=
  154. while (--argc>0) {
  155.   if (**++argv=='-') {
  156.     switch (*++(*argv)) {
  157.       case 'p': 
  158.         vert=pvert;
  159.         horiz=phoriz;
  160.         cross=pcross;
  161.         corner=pcorner;
  162.     break;
  163.       default:
  164.         fprintf(stderr,"treeprint: bad option -%c\n",**argv);
  165.     break;
  166.       }
  167.   }
  168. }
  169.  
  170. @ We play games with a character stack to figure out when to put in vertical
  171. bars.
  172. A vertical bar connects every sibling with its successor, but the last sibling
  173. in a list is followed by blanks, not by vertical bars. The state of 
  174. bar-ness or space-ness for each preceding sibling is recorded in the 
  175. |indent_string| variable, one character (bar or blank) per sibling.
  176.  
  177. @<Global decl...@>=
  178. char indent_string[100]="";
  179.  
  180. @  Children get printed 
  181. before siblings.
  182. We don't bother trying to bring children up to the same line as their parents,
  183. because the UNIX filenames are so long.
  184.  
  185. We define a predicate telling us when a sibling is the last in a series.
  186. @d is_last(S) (S->sibling==NULL)
  187.  
  188. @c
  189. print_node(fp, indent_string, node)
  190.      FILE *fp;
  191.      char *indent_string;
  192.      struct tnode *node;
  193. {
  194.   char string[255];
  195.      int i;
  196.      char *p, *is;
  197.  
  198.   if (node==NULL) {
  199.   }
  200.   else {
  201.     *string='\0';
  202.     for (i=strlen(indent_string); i>0; i--) 
  203.       strcat(string,@,      " |  ");
  204.     strcat(string,@t\ \ @>  " +--");
  205. @<Replace chars in |string| with chars from 
  206.          line-drawing set and from |indent_string|@>;
  207.     fprintf(fp,"%s%s\n",string,node->data);
  208.  
  209. @#
  210.     /* Add vertical bar or space for this sibling (claim |*is=='\0'|) */
  211.     *is++ = (is_last(node) ? ' ' : vert);
  212.     *is=='\0';
  213.    
  214.     print_node(fp, indent_string, node->child); /* extended |indent_string| */
  215.     *--is='\0';
  216.     print_node(fp, indent_string, node->sibling); /* original |indent_string| */
  217.   }
  218.  
  219. }
  220. @ For simplicity, we originally wrote connecting lines with |'|'|, |'+'|, and
  221. |'-'|.
  222. Now we replace those characters with appropriate characters from the 
  223. line-drawing set. 
  224. We take the early vertical bars and replace them with characters from
  225. |indent_string|, and we replace the other characters appropriately.
  226. We are sure to put a |corner|, not a |cross|, on the last sibling in 
  227. a group.
  228. @<Replace chars...@>=
  229.     is=indent_string;
  230.     for (p=string; *p!='\0'; p++) switch(*p) {
  231.        case '|': *p=*is++; break;
  232.        case '+': *p=(is_last(node) ? corner : cross); break;
  233.        case '-': *p=horiz; break;
  234.        default: break;
  235.            }
  236.  
  237.  
  238. @ For this simple implementation, we just write on standard output.
  239.  
  240. @<Write...@>= print_node(stdout, indent_string, root);
  241.  
  242. @*Index.
  243.